<?php
define ( 'IS_CRON', true );

class CronModel {
	private $_sPath;
	
	public function __construct() {
		$this->_setPath ();
	}
	
	private function _setPath() {
		$sPath = Base::getConf ( 'cron.path' );
		$sPath = realpath ( ROOT . '/' . $sPath );
		if (! $sPath) {
			$sPath = ROOT . '/cron';
		}
		$this->_sPath = $sPath;
	}
	
	public function getList() {
		$aFile = Base::$oBase->File_ls ( $this->_sPath );
		foreach ( $aFile as $sFile ) {
			$sFile = basename ( $sFile, '.php' );
			$aInfo = Base::$oBase->Db_get ( 'cron', array ('name' => $sFile ) );
			if (! $aInfo) {
				$aInfo = array ('name' => $sFile, 'status' => 'close' );
			} elseif ($aInfo ['status'] == 'lock') {
				$aInfo ['status'] = 'active';
			}
			$aData [] = $aInfo;
		}
		
		return $aData;
	}
	
	public function update($aData) {
		foreach ( $aData as $sKey => $aRow ) {
			if (is_array ( $aRow )) {
				foreach ( $aRow as $sName => $sRow ) {
					$aInfo [$sName] ['name'] = $sName;
					$aInfo [$sName] [$sKey] = $sRow;
				}
			}
		}
		foreach ( $aInfo as $aRow ) {
			if ($aRow ['status'] == 'active') {
				if ($aInfo ['status'] == 'lock') {
					$aInfo = Base::$oBase->Db_get ( 'cron', array ('name' => $aRow ['name'] ) );
					$aRow ['status'] = 'lock';
				}
			}
			Base::$oBase->Db_replace ( 'cron', $aRow );
		}
	}
	
	public function run() {
		$this->_run ();
	}
	
	private function _run() {
		$sSql = 'SELECT * FROM cron WHERE cro_status = ? AND cro_next_time < ? AND cro_next_time > cro_last_time ORDER BY cro_next_time ASC LIMIT 1';
		$aInfo = Base::$oBase->Db_getOne ( $sSql, array ('active', time () ) );
		if ($aInfo) {
			Base::$oBase->Db_update ( 'cron', array ('status' => 'lock' ), array ('name' => $aInfo ['cro_name'] ) );
			$sScript = $this->_sPath . '/' . $aInfo ['cro_name'] . '.php';
			if (is_file ( $sScript )) {
				include_once $sScript;
				if (function_exists ( 'cron_' . $aInfo ['cro_name'] )) {
					call_user_func ( 'cron_' . $aInfo ['cro_name'] );
				}
			}
			$aUpdate ['last_time'] = time ();
			$aUpdate ['next_time'] = $this->_getNextTime ( $aInfo );
			$aUpdate ['status'] = 'active';
			Base::$oBase->Db_update ( 'cron', $aUpdate, array ('name' => $aInfo ['cro_name'] ) );
			return $this->_run ();
		} else {
			return false;
		}
	}
	
	private function _getNextTime($aInfo, $sAdd = 'minute') {
		if ($sAdd == 'year') {
			$sDate = date ( 'Y-', time () + 86400 * 365 );
		} else {
			$sDate = date ( 'Y-' );
		}
		
		if ($aInfo ['cro_month'] == '*') {
			if ($sAdd == 'month') {
				if (date ( 'm' ) == 12) {
					$sDate = (date ( 'Y' ) + 1) . '-';
				}
				$sDate .= date ( 'm-', time () + 86400 * date ( 't' ) );
			} else {
				$sDate .= date ( 'm-' );
			}
		} else {
			$sDate .= sprintf ( '%02d-', $aInfo ['cro_month'] );
		}
		if ($aInfo ['cro_day'] == '*') {
			if ($sAdd == 'day') {
				$sDate .= date ( 'd ', time () + 86400 );
			} else {
				$sDate .= date ( 'd ' );
			}
		} else {
			$sDate .= sprintf ( '%02d ', $aInfo ['cro_day'] );
		}
		if ($aInfo ['cro_hour'] == '*') {
			if ($sAdd == 'hour') {
				$sDate .= date ( 'H:', time () + 3600 );
			} else {
				$sDate .= date ( 'H:' );
			}
		} else {
			$sDate .= sprintf ( '%02d:', $aInfo ['cro_hour'] );
		}
		if ($aInfo ['cro_minute'] == '*') {
			$sDate .= date ( 'i', time () + 60 );
		} else {
			$sDate .= sprintf ( '%02d', $aInfo ['cro_minute'] );
		}
		$iTime = strtotime ( $sDate );
		if ($iTime < time ()) {
			if ($sAdd == 'minute') {
				return $this->_getNextTime ( $aInfo, 'hour' );
			}
			if ($sAdd == 'hour') {
				return $this->_getNextTime ( $aInfo, 'day' );
			}
			if ($sAdd == 'day') {
				return $this->_getNextTime ( $aInfo, 'month' );
			}
			if ($sAdd == 'month') {
				return $this->_getNextTime ( $aInfo, 'year' );
			}
		}
		return $iTime;
	}
}